package com.jinyang.plugindemo

import android.Manifest
import android.content.ComponentName
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.ContextThemeWrapper
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.databinding.DataBindingUtil
import com.google.gson.Gson
import com.jinyang.introduce_shadow_lib.InitApplication
import com.jinyang.plugindemo.activity.ProxyInterfaceActivity
import com.jinyang.plugindemo.activity.ProxyReflectActivity
import com.jinyang.plugindemo.broadcast.BroadcastUtils
import com.jinyang.plugindemo.contentprovider.PluginUtils
import com.jinyang.plugindemo.databinding.ActivityMainBinding
import com.jinyang.plugindemo.hook.iactivitymanager.HookHelper
import com.jinyang.plugindemo.hook.iactivitymanager.LoadUtils
import com.jinyang.plugindemo.hook.instrumentation.InstrumentationProxy
import com.jinyang.plugindemo.service.StubService
import com.tencent.shadow.dynamic.host.EnterCallback
import com.tencent.shadow.dynamic.host.PluginManager
import dalvik.system.DexClassLoader
import java.io.File

class MainActivity : AppCompatActivity() {
    val FROM_ID_START_ACTIVITY = 1001L
    val FROM_ID_CALL_SERVICE = 1002L
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val arr = arrayOf(
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE
        )
        ActivityCompat.requestPermissions(this, arr, 10)

        val inputStream = assets.open("plugin001.apk")
        File(filesDir.absolutePath, "plugin001.apk").writeBytes(inputStream.readBytes())
        val plugin001Path = File(filesDir.absolutePath, "plugin001.apk").absolutePath
        val nativeLibDir = File(filesDir, "pluginlib").absolutePath
        val dexOutPath = File(filesDir, "dexout").absolutePath
        val pluginClassLoader =
            DexClassLoader(plugin001Path, dexOutPath, nativeLibDir, this::class.java.classLoader)

        PluginUtils.classLoader = pluginClassLoader
        val activityName = "com.jinyang.plugin001.Plugin001Activity"

        val inputStream2 = assets.open("plugin002.apk")
        File(filesDir.absolutePath, "plugin002.apk").writeBytes(inputStream2.readBytes())
        val plugin002Path: String = File(filesDir.absolutePath, "plugin002.apk").absolutePath
        log("pluginPath2:$plugin002Path")
        val pluginClassLoader2 =
            DexClassLoader(plugin002Path, dexOutPath, nativeLibDir, this::class.java.classLoader)
        val activityName2 = "com.jinyang.plugin002.Plugin002Activity"


        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)


        binding.btnPrintResources.setOnClickListener {
            val mResources = LoadUtils.loadResources(
                this,
                resources.assets,
                listOf(plugin001Path, plugin002Path)
            )
            val strAppId = mResources?.getIdentifier("str_app", "string", "com.jinyang.plugindemo")
            log("str_app:" + strAppId?.let { it1 -> mResources.getString(it1) })
            val strPlugin001Id =
                mResources?.getIdentifier("str_plugin001", "string", "com.jinyang.plugindemo")
            log("str_plugin001:" + strPlugin001Id?.let { it1 -> mResources.getString(it1) })
            val strPlugin002Id =
                mResources?.getIdentifier("str_plugin002", "string", "com.jinyang.plugindemo")
            log("str_plugin002:" + strPlugin002Id?.let { it1 -> mResources.getString(it1) })
        }

        binding.btnSingleDexClassLoader.setOnClickListener {
            LoadUtils.loadDex(this, listOf(plugin001Path, plugin002Path))

            val clazzApp = Class.forName("com.jinyang.plugindemo.TestApp")
            val methodApp = clazzApp.getMethod("test")
            methodApp.invoke(clazzApp.newInstance())

            val clazzPlugin001 = Class.forName("com.jinyang.plugin001.TestPlugin001")
            val methodPlugin001 = clazzPlugin001.getMethod("test")
            methodPlugin001.invoke(clazzPlugin001.newInstance())

            val clazzPlugin002 = Class.forName("com.jinyang.plugin002.TestPlugin002")
            val methodPlugin002 = clazzPlugin002.getMethod("test")
            methodPlugin002.invoke(clazzPlugin002.newInstance())
        }

        //手动调用
        // 优点：1. 比较稳定，不需要 hook 系统实现 2. 实现相对简单，不需要对系统内部实现做过多了解
        // 缺点：通过反射效率太低，通过接口需要实现的方法数量很多
        binding.btnStubReflectActivity.setOnClickListener {
            ProxyReflectActivity.startPluginActivity(this, plugin001Path, activityName)
        }
        binding.btnStubInterfaceActivity.setOnClickListener {
            ProxyInterfaceActivity.startPluginActivity(this, plugin001Path, activityName)
        }
        //hook 系统
        // 优点：1. 不需要实现大量接口方法 2. 由于最终还是交给系统去处理，各种处理相对比较完整
        // 缺点：1. 需要适配不同的系统及设备 2. 对开发者要求比较高，需要对系统实现有深入的了解


        binding.btnHookInstrumentation.setOnClickListener {
            InstrumentationProxy.inject(
                this,
                LoadUtils.loadContext(this, resources.assets, listOf(plugin001Path, plugin002Path))
            )
            val intent = Intent()
            intent.setClass(this, pluginClassLoader.loadClass(activityName))
            startActivity(intent)
        }

        binding.btnHookIActivityManager.setOnClickListener {

            LoadUtils.loadDex(this, listOf(plugin002Path))
            HookHelper.hookAMS()
            HookHelper.hookHandler()


            val intent = Intent()
//            intent.setClass(this, pluginClassLoader2.loadClass(activityName2))
//            intent.setClass(this, TestActivity::class.java)
            intent.component = ComponentName("com.jinyang.plugindemo", activityName2)
//            intent.component = ComponentName("com.jinyang.plugin002", activityName2)
            log("intent:${Gson().toJson(intent)}")
            startActivity(intent)
        }
        //调用 插件Service
        val serviceName = "com.jinyang.plugin001.PluginService"
        binding.btnStartService.setOnClickListener {
            StubService.startService(this, pluginClassLoader, serviceName)
        }
        binding.btnStopService.setOnClickListener {
            StubService.stopService(this, pluginClassLoader, serviceName)
        }
        //调用 插件BroadcastReceiver
        val testAction = "com.ljy.action.testBroadcastReceiver"
        val broadcastName = "com.jinyang.plugin001.PluginBroadcastReceiver"
        binding.btnRegisterBroadcastReceiver.setOnClickListener {
            BroadcastUtils.registerBroadcastReceiver(
                this,
                pluginClassLoader,
                testAction,
                broadcastName
            )
        }
        binding.btnSendBroadcast.setOnClickListener {
            sendBroadcast(Intent(testAction))
        }
        binding.btnUnregisterBroadcastReceiver.setOnClickListener {
            BroadcastUtils.unregisterBroadcastReceiver(this, testAction)
        }

        //调用 插件ContentProvider:
        //这里面需要处理的就是，如何转发对应的 Uri 到正确的插件 Provider 中呢，
        // 解决方案是在 Uri 中定义不同的插件路径，
        // 比如 plugin1 的 Uri 对应就是 content://com.zy.stubprovider/plugin1，
        // plugin2 对应的 uri 就是 content://com.zy.stubprovider/plugin2，
        // 然后在 StubContentProvider 中根据对应的 plugin 分发不同的插件 Provider。
        binding.btnQueryContentProvider1.setOnClickListener {
            val uri = Uri.parse("content://com.ljy.StubContentProvider/plugin001")
            val cursor = contentResolver.query(uri, null, null, null, null)
            cursor?.moveToFirst()
            val res = cursor?.getString(0)
            log("provider query res: $res")
            cursor?.close()
        }
        binding.btnQueryContentProvider2.setOnClickListener {
            val uri = Uri.parse("content://com.ljy.StubContentProvider/plugin002")
            val cursor = contentResolver.query(uri, null, null, null, null)
            cursor?.moveToFirst()
            val res = cursor?.getString(0)
            log("provider query res: $res")
            cursor?.close()
        }

        val inputStreamPluginManager = assets.open("plugin-manager.apk")
        File(
            filesDir.absolutePath,
            "plugin-manager.apk"
        ).writeBytes(inputStreamPluginManager.readBytes())
        val inputStreamPlugin003 = assets.open("plugin003.zip")
        File(filesDir.absolutePath, "plugin003.zip").writeBytes(inputStreamPlugin003.readBytes())
        Log.d("LJY_LOG", "MainActivity.pluginZipPath:${ File(filesDir.absolutePath, "plugin003.zip").absolutePath}")
        binding.btnStartPlugin003Activity.setOnClickListener {
            it.isEnabled = false //防止点击重入
            val pluginManager: PluginManager =
                InitApplication.getPluginManager()
            pluginManager.enter(
                this@MainActivity,
                FROM_ID_START_ACTIVITY,
                Bundle(),
                object : EnterCallback {
                    override fun onShowLoadingView(view: View) {
                        this@MainActivity.setContentView(view) //显示Manager传来的Loading页面
                    }

                    override fun onCloseLoadingView() {
                        this@MainActivity.setContentView(binding.root)
                    }

                    override fun onEnterComplete() {
                        it.isEnabled = true
                    }
                })
        }

        binding.btnStartPlugin003Service.setOnClickListener {
            it.isEnabled = false //防止点击重入
            val pluginManager = InitApplication.getPluginManager()
            pluginManager.enter(
                this@MainActivity,
                FROM_ID_CALL_SERVICE.toLong(),
                null,
                null
            )
        }

    }
}

fun log(msg: String) {
    Log.d("LJY_LOG", msg)
}